use super::Result;
use arc_swap::ArcSwapOption;
use clickhouse_rs::Pool;
use once_cell::sync::{Lazy, OnceCell};
use redis::Client;
use serde::{Deserialize, Serialize};
use sqlx::{mysql::MySqlPool, MySql};
use std::{fs, sync::Arc};
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Deserialize, Serialize)]
pub struct Config {
    pub mysql: Database,
    pub clickhouse: Database,
    pub etl_redis: Redis,
    pub ui_redis: Redis,
    pub cron: Cron,
    pub tonic_server_port: u32,
    pub mail: Mail,
    pub write_batch_count: u32,
    pub main_data_source: String,
    pub asse_dicts: Vec<String>,
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Deserialize, Serialize)]
pub struct Database {
    pub host: String,
    pub port: u32,
    pub user: String,
    pub password: String,
    pub database: String,
    pub other_params: String,
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Deserialize, Serialize)]
pub struct Redis {
    pub server: String,
    pub port: u32,
    pub password: String,
}

#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Deserialize, Serialize)]
pub struct Cron {
    pub sec: String,
    pub min: String,
    pub hour: String,
    pub day_of_month: String,
    pub month: String,
    pub day_of_week: String,
    pub year: String,
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Deserialize, Serialize)]
pub struct Mail {
    pub user: String,
    pub pass: String,
    pub host: String,
    pub port: u32,
    pub subject: String,
    pub to_users: Vec<String>,
}
static WORK_PATH: OnceCell<String> = OnceCell::new();
static CONF: Lazy<ArcSwapOption<&'static Config>> = Lazy::new(|| ArcSwapOption::const_empty());
static CH_POOL: Lazy<ArcSwapOption<&'static Pool>> = Lazy::new(|| ArcSwapOption::empty());
static MY_POOL: Lazy<ArcSwapOption<&'static sqlx::pool::Pool<MySql>>> =
    Lazy::new(|| ArcSwapOption::empty());
static ETL_REDIS_CLI: Lazy<ArcSwapOption<&'static Client>> = Lazy::new(|| ArcSwapOption::empty());
static UI_REDIS_CLI: Lazy<ArcSwapOption<&'static Client>> = Lazy::new(|| ArcSwapOption::empty());

pub fn get_work_path() -> &'static str {
    let wp =
        WORK_PATH.get_or_init(|| std::env::var("SHQ_PAS_CONF_PATH").unwrap_or("./".to_string()));
    wp
}
pub async fn init_app_config() -> Result<()> {
    let file_path = format!("{}/config/app.yaml", get_work_path());
    let conf_str = fs::read_to_string(file_path)?;
    let conf: Config = serde_yaml::from_str(&conf_str)?;
    let my_conn = format!(
        "mysql://{}:{}@{}:{}/{}?{}",
        conf.mysql.user,
        conf.mysql.password,
        conf.mysql.host,
        conf.mysql.port,
        conf.mysql.database,
        conf.mysql.other_params
    );
    let my_pool = MySqlPool::connect(&my_conn).await?;
    let my_pool_ref = Box::leak(Box::new(my_pool));
    MY_POOL.store(Some(Arc::new(my_pool_ref)));
    let ch_conn = format!(
        "tcp://{}:{}@{}:{}/{}?{}",
        conf.clickhouse.user,
        conf.clickhouse.password,
        conf.clickhouse.host,
        conf.clickhouse.port,
        conf.clickhouse.database,
        conf.clickhouse.other_params
    );
    let ch_pool = Pool::new(ch_conn);
    let ch_pool_ref = Box::leak(Box::new(ch_pool));
    CH_POOL.store(Some(Arc::new(ch_pool_ref)));
    let ers_conn_str = format!(
        "redis://default:{}@{}:{}/",
        conf.etl_redis.password,conf.etl_redis.server, conf.etl_redis.port
    );
    let etl_redis_cli = redis::Client::open(ers_conn_str.as_str())
        .or_else(|err| Err(anyhow::anyhow!("redis client open error:{}", err)))?;
    let etl_redis_cli_ref = Box::leak(Box::new(etl_redis_cli));
    ETL_REDIS_CLI.store(Some(Arc::new(etl_redis_cli_ref)));
    let urs_conn_str = format!(
        "redis://default:{}@{}:{}/",
        conf.ui_redis.password,conf.ui_redis.server, conf.ui_redis.port
    );
    let ui_redis_cli = redis::Client::open(urs_conn_str.as_str())
        .or_else(|err| Err(anyhow::anyhow!("redis client open error:{}", err)))?;
    let ui_redis_cli_ref = Box::leak(Box::new(ui_redis_cli));
    UI_REDIS_CLI.store(Some(Arc::new(ui_redis_cli_ref)));
    let conf_ref = Box::leak(Box::new(conf));
    CONF.store(Some(Arc::new(conf_ref)));
    Ok(())
}

pub fn get_config() -> &'static Config {
    CONF.load().clone().unwrap().as_ref()
}
pub fn get_my_pool() -> &'static sqlx::pool::Pool<MySql> {
    MY_POOL.load().clone().unwrap().as_ref()
}
pub fn get_ch_pool() -> &'static Pool {
    CH_POOL.load().clone().unwrap().as_ref()
}
pub fn get_etl_redis_cli() -> &'static redis::Client {
    ETL_REDIS_CLI.load().clone().unwrap().as_ref()
}
pub fn get_ui_redis_cli() -> &'static redis::Client {
    UI_REDIS_CLI.load().clone().unwrap().as_ref()
}
